IAM Roleを設定したEC2インスタンスでCloudSearchにアクセスできなかったので対応した件
はじめに
node.jsを用いて、CloudSearch用いる例としては弊社k.ootakiの記事を参照ください。
node.jsでCloudSearchを動かしてみる | Developers.IO
上記の方法では、認証情報(accessKey/secretAccessKey)をconfig.jsonに記載していますが、 EC2上で動かす際は、それらの情報はIAM Role側に持たせることで、認証情報を記述することなく動かすことが可能です。
システムでIAM Roleを用いて動かすといった話は以下の都元の記事を参照ください。
IAMによるAWS権限管理運用ベストプラクティス (2) | Developers.IO
検証を行った環境は以下になります。
- Amazon Linux AMI 2014.09.1 (HVM)
- node.js v0.10.32
- aws-sdk version 2.0.30
ES2上で動かない!?
私の上記のブログを読んだ理解では、IAMRoleを用いて動かすには以下のコードで動くものと想像していました。
sample.js
var AWS = require('aws-sdk'); var config = require('config'); var cloudSearch = new AWS.CloudSearchDomain({ endpoint: config.endpoint, region:config.region }); var params = { query: '角川' }; cloudSearch.search(params, function (err, data) { if (err) { console.log('error', err); } if (data) { if (data.hits.found > 0) { console.log(data.hits.hit[0]); } else { console.log('not found'); } } });
割り当てたIAM Roleは以下のようになります。
IAM Role
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "cloudsearch:*" ], "Effect": "Allow", "Resource": "*" } ] }
上記のコードをIAM Roleを設定したES2で動かそうとすると以下のようになります。
$ node ./sample.js User: anonymous is not authorized to perform
このコードを動かしてみると、User: anonymous is not authorized to performといったエラーが出てしまいます。
要はCredentialがセットされていないようなので、セットしようとおもいます。
クレデンシャルを探せ!
AccessKey/secretAccessKeyをセットすれば動きはしますが、 ベストプラクティスである”APIキーをシステム内にハードコーディングしない”に反してしまうことになります。
上記の例ではCloudSearchDomainのイニシャライズ時にendpointのみを渡していますが、 その際にパラメータとしてクレデンシャルを渡すことができそうです。
File: README — AWS SDK for JavaScript
以下がそのパラメータ候補になります。
- credentials (AWS.Credentials) — the AWS credentials to sign requests with. You can either specify this object, or specify the accessKeyId and secretAccessKey options directly.
- credentialProvider (AWS.CredentialProviderChain) — the provider chain used to resolve credentials if no static credentials property is set.
credentialProviderをセットしてみる。
ベストプラクティスでも紹介されていた、credentialProviderをセットする方法を試してみたいと思います。
sample2.js
var AWS = require('aws-sdk'); var config = require('config'); var chain = new AWS.CredentialProviderChain(); var cloudSearch = new AWS.CloudSearchDomain({ endpoint: config.endpoint, region:config.region, credentialProvider:chain }); var params = { query: '角川' }; cloudSearch.search(params, function (err, data) { if (err) { console.log('error', err); } if (data) { if (data.hits.found > 0) { console.log(data.hits.hit[0]); } else { console.log('not found'); } } });
上記の方法でAWS S3クラス等では、credentialを取得しAPIを実行することができるのですが、 なぜかCloudSearchDomainはだめで結果は最初とかわりません。
Credentinalの情報を取得してセットする。
直接Credential情報をセットすれば動くことはわかっているので、CredentilProviderChainから取得しそれをセットします。
sample3.js
var AWS = require('aws-sdk'); var config = require('config'); var chain = new AWS.CredentialProviderChain(); chain.resolve(function(err, cre) { var cloudSearch = new AWS.CloudSearchDomain({ endpoint: config.endpoint, credentials: cre, region: config.region }); var params = { query: '角川' }; cloudSearch.search(params, function(err, data) { if (err) { console.log('error', err); } if (data) { if (data.hits.found > 0) { console.log(data.hits.hit[0]); } else { console.log('not found'); } }); }); });
上記のコードを動かすことで、ようやくCloudSearchにアクセスすることができました。
$ $ node sample3.js { id: '5', fields: { title: [ '新世紀エヴァンゲリオン(13) (角川コミックス・エース)' ] } }
まとめ
ほかの言語で検証していないので、上記の現象がnode.jsのみにおけるものなのか、 それとも他の開発言語でも該当するのかは不明です。
なお、AWS.CloudSearchクラスはIAM Roleが適用されているEC2インスタンス上から node.jsのSDKからAPIを呼ぶことが可能でした。